﻿/////////////////////////////////////////////////////////////////////////
//                                                                     //
//    MTGProject - A Magic The Gathering Networked Environment         //
//                                                                     //
//  Copyright (c) 2008-2009 by The MTGProject Team (CSC230 & MAT227)   //
//                                                                     //
// This file is part of MTGProject.                                    //
//                                                                     //
// MTGProject is free software; you can redistribute it and/or         //
// modify it under the terms of the GNU Lesser General Public          //
// License as published by the Free Software Foundation; either        //
// version 3 of the License, or (at your option) any later version.    //
//                                                                     //
// This library is distributed in the hope that it will be useful,     //
// but WITHOUT ANY WARRANTY; without even the implied warranty of      //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the       //
// GNU Lesser General Public License for more details.                 //
//                                                                     //
// You should have received a copy of the GNU Lesser General Public    //
// License along with this library; if not, write to the Free Software //
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA           //
// 02111-1307, USA, or contact the author(s):                          //
//                                                                     //
// Ruben Acuna <flyingfowlsoftware@earthlink.net>                      //
//                                                                     //
/////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;

namespace MTGProject
{
    public class UtilMagic
    {
        //is 'dumb' when deciding which NONE to pay with what.
        public static bool CanAffordCost(LinkedList<ManaColor> cost, LinkedList<ManaColor> manaPool)
        {
            LinkedList<ManaColor> copyCost = UtilMagic.CloneEnums<ManaColor>(cost);
            LinkedList<ManaColor> copyManaPool = UtilMagic.CloneEnums<ManaColor>(manaPool);

            //pay specific mana
            foreach (ManaColor mana in copyCost)
            {
                if(mana != ManaColor.NONE)
                    if (copyManaPool.Contains(mana))
                    {
                        copyManaPool.Remove(mana);
                    }
                    else
                        return false;
            }

            //pay colorless 
            foreach (ManaColor mana in copyCost)
            {
                if (mana == ManaColor.NONE)
                    if(copyManaPool.Count > 0)
                        copyManaPool.Remove(copyManaPool.First.Value);
                    else
                        return false;
            }

            return true;
        }

        public static void PayCost(LinkedList<ManaColor> cost, LinkedList<ManaColor> manaPool)
        {
			//Note: We don't actually need to do this but we save time and memory by
			//doing so. Afterall, why go through all the trouble when playing land?
            if (cost.Count > 0)
            {

                LinkedList<ManaColor> costColor = new LinkedList<ManaColor>();
                LinkedList<ManaColor> costColorless = new LinkedList<ManaColor>();

                //Seperate color and colorless mana costs
                foreach (ManaColor mana in cost)
                    if (mana == ManaColor.NONE)
                        costColorless.AddLast(mana);
                    else
                        costColor.AddLast(mana);

                //pay color
                foreach (ManaColor mana in costColor)
                    manaPool.Remove(mana);

                //pay colorless
                foreach (ManaColor mana in costColorless)
                    manaPool.RemoveFirst();
            }
        }
		
		/// <summary>
		/// Check if a given attacker can be blocked by a given defender. Returns
		/// true if the attacker can be blocked.
		/// </summary>
		/// <param name="attacker">Attacking creature.</param>
		/// <param name="defender">Defending creature.</param>
		/// <returns>Can defender block, true or false.</returns>
		public static bool CanBlock(CardCreature attacker, Player attackerPlayer, CardCreature defender, Player defenderPlayer)
		{
			//This method is fairly straight forward. However, the problem is that we have to
			//continue checking each possible keyword until we have disproven the abilty to
			//block beyond a shadow of a doubt. If both the attacker and defender both have
			//FLYING we cannot simply return 'true'. Either creature may have other keywords
			//which still prohibit blocking. 
			
			//Rule: FLYING attackers cannot be blocked.
			if(attacker.HasKeyword(CreatureKeyword.FLYING))
			{
				//Rule: FLYING attackers can be blocked by FLYING defenders.
				//Rule: FLYING attackers can be blocked by REACH defenders.
				if(!(defender.HasKeyword(CreatureKeyword.FLYING) || defender.HasKeyword(CreatureKeyword.REACH)))
				    return false;
			}	
			
			//Rule: FEAR attackers cannot be blocked by defenders.
			if(attacker.HasKeyword(CreatureKeyword.FEAR))
			{
				//Rule Exception: FEAR attackers can be blocked by BLACK or ARTIFACT defenders.
				if(!(defender.Colors.Contains(ManaColor.BLACK) || defender.Colors.Contains(ManaColor.NONE)))
					return false;
			}
			
			//Rule: PLAINS_WALK attackers cannot be blocked if defender has a Plains land in play.
			if(attacker.HasKeyword(CreatureKeyword.PLAINS_WALK))
			{
				foreach(IPermanent permanent in defenderPlayer.Permanents.Cards)
					//Note that the order in which we check type and colors is important. && is 'short
					//circuit', if '&' was use we would evaluate 'is' first and even if that failed we
					//would than also try to cast. The problem there is that we can mis-cast and the
					//runtime will throw an exception.
					if(permanent is CardLand && ((CardLand)permanent).Colors.Contains(ManaColor.WHITE))
						return false;
			}
			
			//Rule: ISLAND_WALK attackers cannot be blocked if defender has a Island land in play. 
			if(attacker.HasKeyword(CreatureKeyword.ISLAND_WALK))
			{
				foreach(IPermanent permanent in defenderPlayer.Permanents.Cards)
					if(permanent is CardLand && ((CardLand)permanent).Colors.Contains(ManaColor.BLUE))
						return false;
			}
			
			//Rule: SWAMP_WALK attackers cannot be blocked if defender has a Swamp land in play.
			if(attacker.HasKeyword(CreatureKeyword.SWAMP_WALK))
			{
				foreach(IPermanent permanent in defenderPlayer.Permanents.Cards)
					if(permanent is CardLand && ((CardLand)permanent).Colors.Contains(ManaColor.BLACK))
						return false;
			}
			
			//Rule: MOUNTAIN_WALK attackers cannot be blocked if defender has a Mountain land in play.
			if(attacker.HasKeyword(CreatureKeyword.MOUNTAIN_WALK))
			{
				foreach(IPermanent permanent in defenderPlayer.Permanents.Cards)
					if(permanent is CardLand && ((CardLand)permanent).Colors.Contains(ManaColor.RED))
						return false;
			}
			
			//Rule: FOREST_WALK attackers cannot be blocked if defender has a Forest land in play.
			if(attacker.HasKeyword(CreatureKeyword.FOREST_WALK))
			{
				foreach(IPermanent permanent in defenderPlayer.Permanents.Cards)
					if(permanent is CardLand && ((CardLand)permanent).Colors.Contains(ManaColor.GREEN))
						return false;
			}
			
			//At this point, the attacker did not have any keywords to prevent it from be blocked.
			
			return true;
		}

        //TODO: check if arrays are passed by referenece.
        public static bool IsDeckLegal(string[] deck, bool fixProblems)
        {
            string spareLand = "";

            //find a spare land if we need to pad the deck or remove cards.
            foreach (string card in deck)
                if (CardIO.IsXMLCardLand(card))
                {
                    spareLand = card;
                    break;
                }

            //Rule: Decks cannot contain more than four copies of any non-land card.
            LinkedList<string> temporaryDeck = new LinkedList<string>();
            LinkedList<string> correctedDeck = new LinkedList<string>();

            //copy deck
            foreach (string card in deck)
                temporaryDeck.AddLast(card);

            while (temporaryDeck.Count > 0)
            {
                string targetCard = temporaryDeck.First.Value;
                int cardCount = 0;

                foreach (string card in deck)
                    if (card.Equals(targetCard))
                        cardCount++;

                while (temporaryDeck.Contains(targetCard))
                    temporaryDeck.Remove(targetCard);

                if (cardCount > Settings.RULE_MAXIMUM_CARD_COPIES)
                    if (fixProblems)
                        cardCount = 4;
                    else
                        return false;

                for (int x = 0; x < cardCount; x++)
                    correctedDeck.AddLast(targetCard);
            }

            deck = new string[correctedDeck.Count];
            for (int x = 0; x < correctedDeck.Count; x++)
            {
                string card = correctedDeck.First.Value;
                deck[x] = card;
                correctedDeck.Remove(card);
            }

            //Rule: Decks must contain at least RULE_MINIMUM_DECK_SIZE cards.
            if (deck.Length < Settings.RULE_MINIMUM_DECK_SIZE)
            {
                if (fixProblems)
                {
                    string[] legalDeck = new string[Settings.RULE_MINIMUM_DECK_SIZE];

                    deck.CopyTo(legalDeck, 0);

                    //pad the deck with the land until it has RULE_MINIMUM_DECK_SIZE cards.
                    for(int x = deck.Length; x < legalDeck.Length; x++)
                        legalDeck[x] = spareLand;

                    deck = legalDeck;
                }
                else
                    return false;
            }

            return true;
        }

        public static LinkedList<T> CloneEnums<T>(LinkedList<T> list)
        {
            LinkedList<T> clone = new LinkedList<T>();

            foreach (T enumeration in list)
                clone.AddLast(enumeration);

            return clone;
        }
    }
}
